package lt.marsrutai.mobile.android.fragment;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import lt.marsrutai.mobile.android.AppContext;
import lt.marsrutai.mobile.android.R;
import lt.marsrutai.mobile.android.activity.PointSelectionMapActivity;
import lt.marsrutai.mobile.android.activity.intent.FastestRoutesResultIntentHolder;
import lt.marsrutai.mobile.android.activity.intent.FastestRoutesSearchIntentHolder;
import lt.marsrutai.mobile.android.adapter.NormalizedStringAdapter;
import lt.marsrutai.mobile.android.exception.DataAccessException;
import lt.marsrutai.mobile.android.manager.ActivityManager;
import lt.marsrutai.mobile.android.model.enums.DateTime;
import lt.marsrutai.mobile.android.service.impl.LocationProviderImpl.GetLocationTask;
import lt.marsrutai.mobile.android.service.impl.LocationProviderImpl.PostExecute;
import lt.marsrutai.mobile.android.util.StringUtils;
import lt.marsrutai.mobile.android.widget.AutoCompleteLocationTextView;
import lt.marsrutai.mobile.android.widget.DialogChoiceItem;
import android.app.Activity;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.TimePicker;

import com.actionbarsherlock.app.SherlockFragment;

public class FastestRoutesSearchFragment extends SherlockFragment {

    private static final DialogChoiceItem ITEM_POINT_ON_MAP = new DialogChoiceItem(R.string.fastest_route_point_on_map, false);
    private static final DialogChoiceItem ITEM_CURRENT_LOCATION = new DialogChoiceItem(R.string.fastest_route_current_location, true);

    private static Map<DialogChoiceItem, SelectionHandler> DEFAULT_DIALOG_CHOICES = new LinkedHashMap<DialogChoiceItem, SelectionHandler>();
    static {
        DEFAULT_DIALOG_CHOICES.put(ITEM_CURRENT_LOCATION, null);
        DEFAULT_DIALOG_CHOICES.put(ITEM_POINT_ON_MAP, new SelectionHandler() {

            @Override
            public void handle(Fragment fragment, int ownerId) {
                fragment.startActivityForResult(new Intent(fragment.getActivity(), PointSelectionMapActivity.class), ownerId);
            }
        });
    }
    
    private ActivityManager manager;

    private List<String> stopNames;

    private String scopeFolder;
    private Calendar selectedDate;
    private DialogChoiceItem[] dialogChoices;

    private Button pickDateButton;
    private Button pickTimeButton;
    private AutoCompleteLocationTextView textViewFrom;
    private AutoCompleteLocationTextView textViewTo;
    private Button searchButton;
    private RadioButton radioDepart;
    private RadioButton radioArrive;

    /**
     * The sole purpose of this array is to assign unique id's (in this case
     * array indices) to all {@link AutoCompleteLocationTextView}s. These indices are
     * used as request codes for starting a sub activities or as a means to
     * communicate between fragments.
     * 
     * @see {@link #onActivityResult(int, int, Intent)}
     * @see {@link #startActivityForResult(Intent, int)}
     * @see {@link #dialogItemSelected(int, DialogChoiceItem)}
     * @see {@link #isSelected(int, DialogChoiceItem)})
     */
    private AutoCompleteLocationTextView[] views;

    @Override
    public void onStart() {
        super.onStart();
        
        // Activity visible - start listening for location updates
        AppContext.getLocationProvider().start();
    }

    @Override
    public void onStop() {
        super.onStart();
        
        // Activity hidden - stop listening for location updates
        AppContext.getLocationProvider().stop();
    }
    
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        textViewFrom = (AutoCompleteLocationTextView) getActivity().findViewById(R.id.point_from);
        textViewTo = (AutoCompleteLocationTextView) getActivity().findViewById(R.id.point_to);
        pickDateButton = (Button) getActivity().findViewById(R.id.entry_date);
        pickTimeButton = (Button) getActivity().findViewById(R.id.entry_time);
        searchButton = (Button) getView().findViewById(R.id.search);
        
        views = new AutoCompleteLocationTextView[] { textViewFrom, textViewTo };

        if (null == manager) {
            manager = new ActivityManager(getActivity(), R.string.no_data_fastest_routes_message);
        }
        
        // Restore state
        receiveItemData(savedInstanceState);
        
        if (null == stopNames || stopNames.isEmpty()) {
            downloadData();
        }
        else {
            setupView();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fastest_route_search, container, false);
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState) {
        FastestRoutesSearchIntentHolder intentHolder = new FastestRoutesSearchIntentHolder(outState);
        intentHolder.setScopeFolder(scopeFolder);
        intentHolder.setDialogChoices(dialogChoices);

        if (selectedDate != null) {
            intentHolder.setCurrentlySelectedDate(selectedDate.getTimeInMillis());
        }
        intentHolder.setStopNames(stopNames);
        
        outState.putAll(intentHolder.getIntent().getExtras());
        super.onSaveInstanceState(outState);
    }

    private void receiveItemData(Bundle bundle) {
        FastestRoutesSearchIntentHolder intentHolder = new FastestRoutesSearchIntentHolder(bundle != null ? bundle : getArguments());
        scopeFolder = intentHolder.getScopeFolder();
        dialogChoices = intentHolder.getDialogChoices();
        stopNames = intentHolder.getStopNames();
        
        long time = intentHolder.getCurrentlySelectedDate();
        if (time != 0) {
            selectedDate = Calendar.getInstance();
            selectedDate.setTime(new Date(intentHolder.getCurrentlySelectedDate()));
        }
    }
    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        FastestRoutesSearchIntentHolder intentHolder = new FastestRoutesSearchIntentHolder(data);

        AutoCompleteLocationTextView view = views[requestCode];
        
        if (resultCode == Activity.RESULT_OK) {
            view.setLocation(intentHolder.getSelectedLocation());
        }
        else {
            view.removeSelectedChoice();
        }
    }

    public void downloadData() {
        Runnable downloadTask = new Runnable() {

            @Override
            public void run() {
                getStopNames(scopeFolder);
            }
        };
        Thread thread = new Thread(downloadTask);
        thread.start();
        manager.showDownloadLoadingBar();
    }

    private void getStopNames(String scopeFolder) {
        try {
            stopNames = AppContext.getDataService().getStopNames(scopeFolder);
        }
        catch (DataAccessException e) {
            manager.runOnUiThread(manager.errorRes);
            return;
        }
        manager.runOnUiThread(returnRes);
    }

    private Runnable returnRes = new Runnable() {

        @Override
        public void run() {
            manager.dismissDownloadLoadingBar();
            setupView();
        }
    };

    public void setupView() {
        setStops();
        setDateTime();
        setSearchButton();

        if (dialogChoices == null) {
            List<DialogChoiceItem> items = new ArrayList<DialogChoiceItem>();
            for (DialogChoiceItem defaultItem : DEFAULT_DIALOG_CHOICES.keySet()) {
                items.add(defaultItem.clone());
            }
            dialogChoices = items.toArray(new DialogChoiceItem[DEFAULT_DIALOG_CHOICES.size()]);
        }

        Button startPointDialog = (Button) getActivity().findViewById(R.id.point_from_dialog);
        startPointDialog.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                DialogFragment dialog = new PointSelectionDialogFragment();
                FastestRoutesSearchIntentHolder holder = new FastestRoutesSearchIntentHolder(getActivity());
                holder.setDialogTitle(getString(R.string.fastest_route_choose_start_point_title));
                holder.setDialogChoices(dialogChoices);
                holder.setDialogOwnerId(Arrays.asList(views).indexOf(textViewFrom));
                
                dialog.setArguments(holder.getIntent().getExtras());
                dialog.show(getActivity().getSupportFragmentManager(), "StartPointSelectionDialogFragment");
            }
        });        
        Button finishPointDialog = (Button) getActivity().findViewById(R.id.point_to_dialog);
        finishPointDialog.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                DialogFragment dialog = new PointSelectionDialogFragment();
                FastestRoutesSearchIntentHolder holder = new FastestRoutesSearchIntentHolder(getActivity());
                holder.setDialogTitle(getString(R.string.fastest_route_choose_end_point_title));
                holder.setDialogChoices(dialogChoices);
                holder.setDialogOwnerId(Arrays.asList(views).indexOf(textViewTo));
                
                dialog.setArguments(holder.getIntent().getExtras());
                dialog.show(getActivity().getSupportFragmentManager(), "FinishPointSelectionDialogFragment");
            }
        });

        radioDepart = (RadioButton) getActivity().findViewById(R.id.radio_depart);
        radioDepart.setChecked(true);
        radioArrive = (RadioButton) getActivity().findViewById(R.id.radio_arrive);
    }

    private void setSearchButton() {
        searchButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                sendItemData();
            }
        });
    }

    private void sendItemData() {
        String start = textViewFrom.getText().toString();
        String finish = textViewTo.getText().toString();

        if (StringUtils.isEmpty(start)) {
            manager.makeToast(getActivity(), getString(R.string.no_start_point));
            return;
        }

        if (StringUtils.isEmpty(finish)) {
            manager.makeToast(getActivity(), getString(R.string.no_finish_point));
            return;
        }
        
        /* If any of the selections were "current location", then proceed to acquire current location
         * and invoke search when finished.
         */
        if (textViewFrom.isSelected(ITEM_CURRENT_LOCATION) || textViewTo.isSelected(ITEM_CURRENT_LOCATION)) {
            final GetLocationTask getLocationTask = (GetLocationTask) new GetLocationTask(getActivity(), new PostExecute() {

                @Override
                public void handle(Location result, String errorMessage) {
                    if (result == null) {
                        // Location was not resolved - display errors
                        manager.makeToast(getActivity(), errorMessage);
                    }
                    else {
                        if (textViewFrom.isSelected(ITEM_CURRENT_LOCATION)) {
                            textViewFrom.setLocation(result);
                        }
                        if (textViewTo.isSelected(ITEM_CURRENT_LOCATION)) {
                            textViewTo.setLocation(result);
                        }
                        performSearch();
                    }
                }
            });
            getLocationTask.execute();
            
            return;
        }

        // Normal flow - "current location was not selected"
        performSearch();
    }

    protected void performSearch() {
        Location locationFrom = textViewFrom.getLocation();
        Location locationTo = textViewTo.getLocation();
        String start = locationFrom != null ? formatLocation(locationFrom) : textViewFrom.getText().toString();
        String finish = locationTo != null ? formatLocation(locationTo) : textViewTo.getText().toString();

        FastestRoutesResultIntentHolder intentHolder = new FastestRoutesResultIntentHolder(getActivity());
        intentHolder.setScopeFolder(scopeFolder);
        intentHolder.setStopFrom(start);
        intentHolder.setStopTo(finish);
        intentHolder.setDateTime(pickDateButton.getText() + "T" + pickTimeButton.getText());
        intentHolder.setIsArrivalInd(radioArrive.isChecked());
        startActivity(intentHolder.getIntent());
    }
    
    private String formatLocation(Location location) {
        return String.format(Locale.US, "@%.8f;%.8f", location.getLatitude(), location.getLongitude());
    }

    private void setStops() {
        NormalizedStringAdapter<String> stop_from = new NormalizedStringAdapter<String>(getActivity(),
                android.R.layout.simple_dropdown_item_1line, stopNames);
        textViewFrom.setThreshold(2);
        textViewFrom.setAdapter(stop_from);
        textViewFrom.setOnEditorActionListener(new OnEditorActionListener() {

            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
                    textViewTo.requestFocus();
                    // Must return true here to consume event
                    return true;
                }
                return false;
            }
        });

        NormalizedStringAdapter<String> stop_to = new NormalizedStringAdapter<String>(getActivity(),
                android.R.layout.simple_dropdown_item_1line, stopNames);
        textViewTo.setThreshold(2);
        textViewTo.setAdapter(stop_to);
        textViewTo.setOnEditorActionListener(new OnEditorActionListener() {

            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
                    sendItemData();
                    // Must return true here to consume event
                    return true;
                }
                return false;
            }
        });
    }

    private void setDateTime() {
        if (selectedDate == null) {
            DateTime dateTime = new DateTime();
            selectedDate = new GregorianCalendar(dateTime.getCurrentYear(), dateTime.getCurrentMonth(), dateTime.getCurrentDay(), dateTime.getCurrentHour(), dateTime.getCurrentMinute());
        }
        
        pickDateButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                DialogFragment dialog = new DatePickerFragment();
                
                FastestRoutesSearchIntentHolder holder = new FastestRoutesSearchIntentHolder(getActivity());
                holder.setCurrentlySelectedDate(selectedDate.getTimeInMillis());
                dialog.setArguments(holder.getIntent().getExtras());
                
                dialog.show(getActivity().getSupportFragmentManager(), "datePicker");
            }
        });
        pickTimeButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                DialogFragment dialog = new TimePickerFragment();
                
                FastestRoutesSearchIntentHolder holder = new FastestRoutesSearchIntentHolder(getActivity());
                holder.setCurrentlySelectedDate(selectedDate.getTimeInMillis());
                dialog.setArguments(holder.getIntent().getExtras());
                
                dialog.show(getActivity().getSupportFragmentManager(), "timePicker");
            }
        });
        updateTimeDisplay();
        updateDateDisplay();
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        selectedDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
        selectedDate.set(Calendar.MINUTE, minute);
        updateTimeDisplay();
    }

    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        selectedDate.set(Calendar.YEAR, year);
        selectedDate.set(Calendar.MONTH, monthOfYear);
        selectedDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        updateDateDisplay();
    }

    private void updateTimeDisplay() {
        pickTimeButton.setText(new StringBuilder().append(pad(selectedDate.get(Calendar.HOUR_OF_DAY))).append(":").append(pad(selectedDate.get(Calendar.MINUTE))));
    }

    private void updateDateDisplay() {
        pickDateButton.setText(new StringBuilder().append(selectedDate.get(Calendar.YEAR)).append("-").append(pad(selectedDate.get(Calendar.MONTH) + 1)).append("-").append(pad(selectedDate.get(Calendar.DAY_OF_MONTH))));
    }

    private static String pad(int c) {
        if (c >= 10) {
            return String.valueOf(c);
        }
        else {
            return "0" + String.valueOf(c);
        }
    }

    /**
     * Callback from location selection dialog
     * 
     * @param id 
     * @param ownerId Owner view id
     */
    public void dialogItemSelected(int ownerId, DialogChoiceItem item) {
        SelectionHandler handler = DEFAULT_DIALOG_CHOICES.get(item);
        if (handler != null) {
            handler.handle(this, ownerId);
        }

        views[ownerId].setSelectedChoice(item);
    }

    /**
     * Callback from location selection dialog.
     * Delegates call to {@link AutoCompleteLocationTextView#isSelected(DialogChoiceItem)}
     * 
     * @param ownerId
     * @param item
     * @return
     */
    public boolean isSelected(int ownerId, DialogChoiceItem item) {
        return views[ownerId].isSelected(item);
    }

    /**
     * Handles {@link DialogChoiceItem} selection events
     * 
     * @author Justas
     */
    private interface SelectionHandler {
        
        void handle(Fragment fragment, int ownerId);
    }
}
